/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package InternetHax;

import java.util.Vector;

/**
 * A Map represents the entire game world. It is constructed of {@link Tile} objects
 * and holds all Apps, Monsters and the Hero. Care should be taken when clearing the map
 * or constructing an new map in order to prevent memory leaks
 * @author LoginError
 */
public class Map {

    public Tile[][] tilemap;
    short xsize, ysize;
// Vector creatures;
// Vector items;
    Pathfinder pathfinder;

    public Map(short xsize, short ysize) {
        tilemap = new Tile[xsize][ysize];
        this.xsize = xsize;
        this.ysize = ysize;

        for (int y = 0; y < ysize; y++) {
            for (int x = 0; x < xsize; x++) {
                tilemap[x][y] = new Tile(Constants.TILE_UNPASSABLE);
            }
        }
        pathfinder = new Pathfinder(this);
    }

    /**
     * Uses the pathfinder to check if there is a possible path between two tiles on the map
     * @returns true if it is possible to move from one point to the other by traveling in either Up, Down, Left, or Right directions, false otheriwse 
     */
    public boolean isThereAPath(Point2d start, Point2d end) {

        int result = pathfinder.FindPath(false, false, false, start.x, start.y, end.x, end.y);
        if (result == Pathfinder.found) {
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * Finds a path between two given tiles on the map
     * @param ignoreWeights true if the pathfinder should treat all tiles as equal, false otherwise
     * @param trailblazer true if the pathfinder should construct it's path using unwalkable tiles (used when creating paths between rooms), false otherwise
     * @param firstpoint start point
     * @param otherPoint destination points
     * @return a Vector containing several Point2d objects making up the path from firstpoint to otherPoint, the vector will be empty if no path was found
     */
    public Vector getPath(boolean ignoreWeights, boolean trailblazer, Point2d firstPoint, Point2d otherPoint) {
        pathfinder.FindPath(ignoreWeights, trailblazer, false, firstPoint.x, firstPoint.y, otherPoint.x, otherPoint.y);
        return pathfinder.getPath();
    }

    /**
     * returns the tile ID of the tile specified by an x, y location
     * @param x the x coordinate of the tile, measured in tiles
     * @param y the y coordinate of the tile, measured in tiles
     * @return the tile ID of the found tile, will return TILE_UNPASSABLE if the tile is not within the map
     */
    public int getTileType(int x, int y) {
        if (tileIsInMap(x, y)) {
            return tilemap[x][y].getTileType();
        }
        else {
            return Constants.TILE_UNPASSABLE;
        }
    }

    /**
     * Sets the tile type at the specified location
     * @param x the x coordinate of the tile, measured in tiles
     * @param y the y coordinate of the tile, measured in tiles
     * @param tile the ID to set the tile to
     */
    public void setTileType(int x, int y, int tile) {
        if (tileIsInMap(x, y)) {
            tilemap[x][y].setTileType(tile);
        }
    }

    /**
     * Checks to see if an x,y location is within the map.
     * Use this to prevent {@link ArrayIndexOutOfBoundsException}s from appearing all over the place
     * @param x x coordinate of the tile, measured in tiles
     * @param y y coordinate of the tile, measured in tiles
     * @return true if tile is within the map, false otherwise
     */
    public boolean tileIsInMap(int x, int y) {
        if (x >= 0 && x < xsize && y >= 0 && y < ysize) {
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * returns the walkable status of the tile at the given coords
     * @param x x coordinate of the tile, measured in tiles
     * @param y y coordinate of the tile, measured in tiles
     * @return true if the tile is walkable, false otherwise
     */
    public boolean isWalkable(int x, int y) {
        if (tileIsInMap(x, y)) {
            return (tilemap[x][y].getTileType() >= 100 ? false : true);
        }
        else {
            return false;
        }
    }

    /**
     * Prints the map based on walkablility to standard out, used for debugging
     */
    public void printWalkableDebug() {
        System.out.println("-------------MAP BEGINS---------------");
        for (int y = 0; y < ysize; y++) {
            for (int x = 0; x < xsize; x++) {

                int tiletype = tilemap[x][y].getTileType();
                switch (tiletype) {
                    case Constants.TILE_UNPASSABLE:
                        System.out.print(" ");
                        break;

                    case Constants.TILE_CORRUPTED_SPACE:
                        System.out.print("*");
                        break;

                    case Constants.TILE_CORRUPTED_FLOOR:
                        System.out.print("%");
                        break;

                    case Constants.TILE_FLOOR:
                        System.out.print("#");
                        break;

                    //case Tile.TILE_PATH_DEBUG:
                    //    System.out.print("@");
                    //break;
                    }
            }
            System.out.println();
        }
        System.out.println("-------------MAP ENDS-----------------");
    }

    /**
     * Prints the map to standard out as a list of tile id's. Used for debugging.
     */
    public void printNumberDebug() {
        for (int y = 0; y < ysize; y++) {
            for (int x = 0; x < xsize; x++) {
                if (tilemap[x][y].getTileType() < 100) {
                    System.out.print("00");
                }
                else if (tilemap[x][y].getTileType() < 100) {
                    System.out.print("0");
                }
                System.out.print(tilemap[x][y] + "|");
            }
            System.out.println();
        }
    }

    /**
     * Moves an actor from it's current location to a new one 
     * @param theactor the actor to move
     * @param oldx the actor's current location, x coordinate
     * @param oldy the actor's current location, x coordinate
     * @param x the x coordinate of the tile to move the actor to
     * @param y the y coordinate of the tile to move the actor to
     */
    public void moveActorToTile(Actor theactor, int oldx, int oldy, int x, int y) {
        //TODO: change this to int, return success or failure
        if (tileIsInMap(x, y)) {
            this.tilemap[oldx][oldy].setActorOnTile(null);
            setActorOnTile(theactor, x, y);
        }
    }

    /**
     * Places theactor on the tile specified by x, y
     * @param theactor the actor to place within the map
     * @param x the x coordinate of the tile to place the actor on
     * @param y the y coordinate of the tile to place the actor on
     */
    public void setActorOnTile(Actor theactor, int x, int y) {
        if (tileIsInMap(x, y)) {
            this.tilemap[x][y].setActorOnTile(theactor);
        }
    }

    /**
     * returns the actor currently on the tile indicated by x,y
     * @param x the x coordinate of the tile to check
     * @param y the y coordinate of the tile to check
     * @return the Actor found on the tile, null if tile is outside of map, or no actor found.
     */
    public Actor getActorOnTile(int x, int y) {
        if (tileIsInMap(x, y)) {
            return this.tilemap[x][y].getActorOnTile();
        }
        else {
            return null;
        }
    }

    public void debugTile(int x, int y)
    {
        Toolbox.printDebug("\nInfo for Tile located at ("+x+","+ y+ ")"+this.tilemap[x][y].toString()+"\n\nEnd Tile Info\n");
    }

    /**
     * Checks to see if there is an actor on the tile indicated by x,y
     * @param x the x coordinate of the tile to check
     * @param y the y coordinate of the tile to check
     * @return true if an actor is found on that tile, false if tile is outside of map or no actor found
     */
    public boolean isThereActorOnTile(int x, int y) {
        if (this.tilemap[x][y].getActorOnTile() == null) {
            return false;
        }
        else {
            return true;
        }
    }
}